package service

import (
	"fmt"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/beego/beego/v2/client/orm"
	"github.com/beego/beego/v2/server/web/context"
	"github.com/google/uuid"
	smnModel "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/smn/v2/model"
	oriError "github.com/pkg/errors"
	"scase.io/application-auto-scaling-service/pkg/api/errors"
	"scase.io/application-auto-scaling-service/pkg/api/model"
	"scase.io/application-auto-scaling-service/pkg/cloudresource"
	"scase.io/application-auto-scaling-service/pkg/common"
	"scase.io/application-auto-scaling-service/pkg/db"
	"scase.io/application-auto-scaling-service/pkg/redis"
	"scase.io/application-auto-scaling-service/pkg/utils/logger"
)

type EventService struct {
	Ctx       *context.Context
	tLogger   *logger.FMLogger
	smnClient *cloudresource.SmnResourceController
	projectId string
}

const (
	FieldResourceType             = "resource_type"
	FieldTraceId                  = "id"
	FieldTraceState               = "trace_state"
	FieldRecordTime               = "record_time"
	FieldStartTime                = "start_time"
	FieldEndTime                  = "end_time"
	FieldEventType                = "event_type"
	DefaultTopicDisplayName       = "HWC-SAC-Game-Flex-Match"
	DefaultMessageSubject         = "GameFlexMatch Event Alarm"
	TraceStateNormal              = "NORMAL"
	TraceStateWarning             = "WARNING"
	TraceStateIncident            = "INCIDENT"
	DefaultMaxSubscriptionsCount  = 50
	RedisTraceStateKey            = "trace-state"
	EventClearCountToSleep        = 50
	EventClearCountToSleepSeconds = 1
	defaultSubscriptionLimit      = 10
	defaultSubscriptionOffset     = 0
)

func NewEventService(projectId string, ctx *context.Context, tLogger *logger.FMLogger) (*EventService, error) {
	smnC, err := cloudresource.NewSmnResourceController(projectId, tLogger)
	if err != nil {
		return nil, err
	}
	return &EventService{
		Ctx:       ctx,
		tLogger:   tLogger,
		smnClient: smnC,
		projectId: projectId,
	}, nil
}

// CreateEvent: 注册一个事件
func (s *EventService) CreateEvent(req *model.CreateEventRequest) (
	*model.ShowEventResponse, *errors.ErrorResp) {
	event := s.BuildEventModel(req)
	eventOpt := db.GetEventTableOpt()
	err := eventOpt.Insert(event)
	if err != nil {
		s.tLogger.Error("insert event %s into db error: %+v", event.Id, err)
		return nil, errors.NewErrorRespWithMessage(errors.ServerInternalError, err.Error())
	}
	s.tLogger.Info("success to insert event %s into db", event.Id)
	// 如果事件级别是严重，则通知发布消息
	go s.TryPublishMessage(event)
	return s.BuildEventResp(event), nil
}

func (s *EventService) TryPublishMessage(event *db.Event) {
	// 判断是否需要发送消息，以redis存储的事件名为判断条件，若事件名已存在则不发送，若不存在则发送并写入redis，存储时间为30s
	rCli := redis.GetRedisClient()
	defer redis.CloseRedisClient(rCli)
	exist, err := rCli.Exists(event.Name).Result()
	if err != nil {
		s.tLogger.Error("judge event %s is or not exists err: %+v", event.Name, err)
		return
	}
	if exist == 1 {
		val, errG := rCli.Get(event.Name).Result()
		if errG != nil {
			s.tLogger.Error("get event %s err: %+v", event.Name, err)
			return
		}
		ttl, errT := rCli.TTL(event.Name).Result()
		if errT != nil {
			s.tLogger.Error("get event %s ttl err: %+v", event.Name, err)
			return
		}
		s.tLogger.Info("event name %s message have trigged %s times in 30 seconds, would step it", event.Name, val)
		valInt, errS := strconv.Atoi(val)
		if errS != nil {
			s.tLogger.Error("atoi val %s err: %+v", val, err)
			return
		}
		errS = rCli.Set(event.Name, valInt+1, ttl).Err()
		if err != nil {
			s.tLogger.Error("set event %s and times %d into redis err: %+v", event.Name, valInt, errS)
			return
		}
		return
	}
	errS := rCli.Set(event.Name, 1, 30*time.Second).Err()
	if err != nil {
		s.tLogger.Error("set event %s and times %d into redis err: %+v", event.Name, 1, errS)
		return
	}
	mOpt := db.GetMessageTableOpt()
	// 查询project上是否已存在主题，存在则报错不能重复创建
	ms, err := mOpt.Show(s.projectId)
	if err != nil {
		s.tLogger.Error("project %s has not existed topic", s.projectId)
		return
	}
	traceStates := strings.Split(ms.NoticeTraceStatus, ",")
	for _, traceState := range traceStates {
		if traceState == event.TraceState {
			s.PublishMessage(DefaultMessageSubject, event)
			break
		}
	}
}

// DeleteEvent: 删除事件
func (s *EventService) DeleteEvent(eventId string) *errors.ErrorResp {
	if s.projectId == "" || eventId == "" {
		s.tLogger.Error("delete event param project_id or event_id not nil")
		return errors.NewErrorResp(errors.RequestParamsError)
	}
	eventOpt := db.GetEventTableOpt()
	cond := orm.NewCondition()
	cond = cond.And("project_id", s.projectId)
	cond = cond.And("id", eventId)
	if !eventOpt.CheckExist(cond) {
		s.tLogger.Error("event id %s does not exist", eventId)
		return errors.NewErrorRespWithMessage(errors.DbError, fmt.Sprintf("event does not exist"))
	}
	err := eventOpt.Delete(eventId)
	if err != nil {
		s.tLogger.Error("delete event %s from db error: %+v", eventId, err)
		return errors.NewErrorRespWithMessage(errors.DbError, fmt.Sprintf("delete event from db error:%+v", err))
	}
	s.tLogger.Info("success to delete event %s from db", eventId)
	return nil
}

// ListEvents: 获取事件列表
func (s *EventService) ListEvents(param *model.QueryEventsParam, offset int, limit int) (
	*model.ListEventsResponse, *errors.ErrorResp) {
	cond := orm.NewCondition()
	cond = cond.And("project_id", s.projectId)
	if param.TraceId != "" {
		cond = cond.And(FieldTraceId, param.TraceId)
	}
	if param.ResourceType != "" {
		cond = cond.And(FieldResourceType, param.ResourceType)
	}
	if param.TraceState != "" {
		cond = cond.And(FieldTraceState, param.TraceState)
	}
	if param.StartTime != "" {
		cond = cond.And(fmt.Sprintf("%s__gte", FieldRecordTime), param.StartTime)
	}
	if param.EndTime != "" {
		cond = cond.And(fmt.Sprintf("%s__lte", FieldRecordTime), param.EndTime)
	}
	eventOpt := db.GetEventTableOpt()
	events, err := eventOpt.List(cond, offset, limit)
	if err != nil {
		s.tLogger.Error("list events from db err: %+v", err)
		return nil, errors.NewErrorRespWithMessage(errors.DbError, fmt.Sprintf("get event from db error:%+v", err))
	}
	count, err := eventOpt.Count(cond)
	if err != nil {
		s.tLogger.Error("get events count from db err: %+v", err)
		return nil, errors.NewErrorRespWithMessage(errors.DbError, fmt.Sprintf("get event from db error:%+v", err))
	}
	resp := &model.ListEventsResponse{
		TotalCount: count,
		Count:      len(*events),
		Events:     []model.ShowEventResponse{},
	}
	for _, event := range *events {
		resp.Events = append(resp.Events, *s.BuildEventResp(&event))
	}
	s.tLogger.Info("success to list events from db")
	return resp, nil
}

// ShowEvent: 获取某个事件的详情
func (s *EventService) ShowEvent(id string) (*model.ShowEventResponse, *errors.ErrorResp) {
	if s.projectId == "" || id == "" {
		s.tLogger.Error("show event param project_id or event_id not nil")
		return nil, errors.NewErrorResp(errors.RequestParamsError)
	}
	eventOpt := db.GetEventTableOpt()
	cond := orm.NewCondition()
	cond = cond.And("project_id", s.projectId)
	cond = cond.And("id", id)
	if !eventOpt.CheckExist(cond) {
		s.tLogger.Error("event id %s does not exist", id)
		return nil, errors.NewErrorRespWithMessage(errors.DbError, fmt.Sprintf("event does not exist"))
	}
	event, err := eventOpt.Show(id)
	if err != nil {
		s.tLogger.Error("list events from db err: %+v", err)
		return nil, errors.NewErrorRespWithMessage(errors.DbError, fmt.Sprintf("get event from db error:%+v", err))
	}
	s.tLogger.Info("success to get event %s from db", id)
	return s.BuildEventResp(event), nil
}

// BuildEventModel: 构建事件的model
func (s *EventService) BuildEventModel(req *model.CreateEventRequest) *db.Event {
	return &db.Event{
		Id:           uuid.NewString(),
		Name:         req.Name,
		ResourceType: req.ResourceType,
		TraceService: req.TraceService,
		ResourceId:   req.ResourceId,
		ResourceName: req.ResourceName,
		TraceState:   req.TraceState,
		EventType:    req.EventType,
		ProjectId:    s.projectId,
		SourceIp:     req.SourceIp,
		Details:      req.Details,
		SmnMessage:   req.SmnMessage,
	}
}

// BuildEventResp: 构建事件的响应体
func (s *EventService) BuildEventResp(modelEvent *db.Event) *model.ShowEventResponse {
	return &model.ShowEventResponse{
		Id:           modelEvent.Id,
		Name:         modelEvent.Name,
		ResourceType: modelEvent.ResourceType,
		TraceService: modelEvent.TraceService,
		ResourceId:   modelEvent.ResourceId,
		ResourceName: modelEvent.ResourceName,
		TraceState:   modelEvent.TraceState,
		EventType:    modelEvent.EventType,
		SourceIp:     modelEvent.SourceIp,
		Details:      modelEvent.Details,
		NoticeState:  modelEvent.NoticeState,
		RecordTime:   modelEvent.RecordTime.Format(common.TimeLayout),
		SmnMessage:   modelEvent.SmnMessage,
	}
}

// CreateTopic: 创建主题
func (s *EventService) CreateTopic(req *model.CreateTopicRequest) (
	*model.ShowMessageResponse, *errors.ErrorResp) {
	s.tLogger.Info("receive create topic request: %s", req.Name)
	mOpt := db.GetMessageTableOpt()
	// 查询project上是否已存在主题，存在则报错不能重复创建
	ms, err := mOpt.Show(s.projectId)
	if err == nil {
		s.tLogger.Error("project %s has existed topic %s", s.projectId, ms.Name)
		return nil, errors.NewErrorRespWithMessage(errors.TopicExistedError, fmt.Sprintf("A topic:%s already exists in DB", ms.Name))
	}
	if !oriError.Is(err, orm.ErrNoRows) {
		s.tLogger.Error("query topic from db err: %+v", err)
		return nil, errors.NewErrorRespWithMessage(errors.DbError, fmt.Sprintf("get topic from db error:%+v", err))
	}
	// 若不存在则执行创建流程
	// 主题的显示名采用默认
	req.DisplayName = DefaultTopicDisplayName
	mm := s.BuildMessageModel(req)
	resp, err := s.smnClient.CreateTopic(req)
	if err != nil {
		s.tLogger.Error("create topic %s error %+v", req.Name, err)
		errStr := fmt.Sprintf("Create topic error %s", err.Error())
		mm.StateAndReason = fmt.Sprintf("Failed: %s", errStr)
		return nil, errors.NewErrorRespWithMessage(errors.EventSmnError,
			fmt.Sprintf("create topic %s; update topic state %+v", errStr, err))
	} else {
		mm.TopicUrn = *resp.TopicUrn
		mm.StateAndReason = "Success"
	}
	err = mOpt.Insert(mm)
	if err != nil {
		s.tLogger.Error("insert topic into db err: %+v", err)
		return nil, errors.NewErrorRespWithMessage(errors.DbError, fmt.Sprintf("get topic from db error:%+v", err))
	}
	subcriptionsRsp, err := s.smnClient.ListSubscriptionsByTopic(*resp.TopicUrn, defaultSubscriptionLimit, defaultSubscriptionOffset)
	if err != nil {
		s.tLogger.Error("list subcriptions error %+v after success to create topic", req.Name, err)
		return nil, errors.NewErrorRespWithMessage(errors.EventSmnError, "List subcriptions error")
	}
	return s.BuildMessageResp(mm, subcriptionsRsp), nil
}

func (s *EventService) BuildMessageModel(req *model.CreateTopicRequest) *db.Message {
	return &db.Message{
		ProjectId:           s.projectId,
		Name:                req.Name,
		DisplayName:         req.DisplayName,
		EnterpriseProjectId: req.EnterpriseProjectId,
		NoticeTraceStatus:   strings.Join(req.NoticeTraceStatus, ","),
	}
}

func (s *EventService) BuildMessageResp(mm *db.Message, subs *smnModel.ListSubscriptionsByTopicResponse) *model.ShowMessageResponse {
	subscriptionCount := 0
	if subs.SubscriptionCount != nil{
		subscriptionCount = int(*subs.SubscriptionCount)
	}
	var resp = &model.ShowMessageResponse{
		Name:                mm.Name,
		TopicUrn:            mm.TopicUrn,
		DisplayName:         mm.DisplayName,
		EnterpriseProjectId: mm.EnterpriseProjectId,
		SubscriptionCount:   subscriptionCount,
		StateAndReason:      mm.StateAndReason,
		CreationTime:        mm.CreationTime.Format(common.TimeLayout),
		Subscriptions:       []model.Subcription{},
		NoticeTraceStatus:   strings.Split(mm.NoticeTraceStatus, ","),
	}
	for _, sub := range *subs.Subscriptions {
		resp.Subscriptions = append(resp.Subscriptions, *s.BuildSubscription(&sub))
	}
	return resp
}

func (s *EventService) BuildSubscription(si *smnModel.ListSubscriptionsItem) *model.Subcription {
	return &model.Subcription{
		Protocol:       si.Protocol,
		Endpoint:       si.Endpoint,
		SubcriptionUrn: si.SubscriptionUrn,
		Remark:         si.Remark,
		Status:         int(si.Status),
	}
}

// DeleteTopic: 删除主题
func (s *EventService) DeleteTopic() *errors.ErrorResp {
	s.tLogger.Info("receive delete topic request: project id %s", s.projectId)
	mOpt := db.GetMessageTableOpt()
	mm, err := mOpt.Show(s.projectId)
	if err != nil {
		s.tLogger.Error("query topic error: %+v", err)
		return errors.NewErrorRespWithMessage(errors.DbError, fmt.Sprintf("get topic from db error:%+v", err))
	}
	if err := s.DeleteTopicFromSmn(mm); err != nil {
		s.tLogger.Info("delete topic %s from smn err %+v", mm.TopicUrn, err)
		return errors.NewErrorRespWithMessage(errors.EventSmnError, fmt.Sprintf("delete topic err: %s", err.Error()))
	}
	err = mOpt.Delete(s.projectId)
	if err != nil {
		s.tLogger.Error("delete topic error: %+v", err)
		return errors.NewErrorRespWithMessage(errors.DbError, fmt.Sprintf("delete topic from db error:%+v", err))
	}

	s.tLogger.Info("success to delete topic, project id %s", s.projectId)
	return nil
}

// DeleteTopicFromSmn: 从smn上删除关联的主题
func (s *EventService) DeleteTopicFromSmn(mm *db.Message) error {
	if mm.TopicUrn == "" {
		return nil
	}
	resp, err := s.smnClient.ListTopicByEpAndName(mm.EnterpriseProjectId, mm.Name)
	if err != nil {
		return err
	}
	for _, topic := range *resp.Topics {
		if topic.TopicUrn != mm.TopicUrn {
			continue
		}
		_, err := s.smnClient.DeleteTopic(mm.TopicUrn)
		if err != nil {
			return err
		}
		break
	}
	s.tLogger.Info("success to delete topic %s from smn", mm.TopicUrn)
	return nil
}

// ShowTopic: 显示主题
func (s *EventService) ShowTopic() (*model.ShowMessageResponse, *errors.ErrorResp) {
	s.tLogger.Info("receive show topic request: project id %s", s.projectId)
	mOpt := db.GetMessageTableOpt()
	// 查询project上是否已存在主题，存在则报错不能重复创建
	ms, err := mOpt.Show(s.projectId)
	if err != nil {
		if oriError.Is(err, orm.ErrNoRows) {
			s.tLogger.Error("topic not found: %+v", err)
			return &model.ShowMessageResponse{}, nil
		}
		s.tLogger.Error("show topic from db err %+v, project id %s", err, s.projectId)
		return nil, errors.NewErrorRespWithMessage(errors.DbError, err.Error())
	}
	// topicUrn创建失败，直接返回数据库中存储的数据，需要手动删除后再次创建
	if ms.TopicUrn == "" {
		s.tLogger.Error("topic %s create error")
		var subsResp = &smnModel.ListSubscriptionsByTopicResponse{
			Subscriptions: &[]smnModel.ListSubscriptionsItem{},
		}
		return s.BuildMessageResp(ms, subsResp), nil
	}
	limit := common.GetLimit(s.Ctx, s.tLogger)
	offset := common.GetOffset(s.Ctx, s.tLogger)
	subs, err := s.smnClient.ListSubscriptionsByTopic(ms.TopicUrn, int32(limit), int32(offset))
	if err != nil {
		s.tLogger.Error("list subcriptions error %+v topic urn", err, ms.TopicUrn)
		ms.StateAndReason = fmt.Sprintf("show topic subcriptions err, you can try to delete and recreate topic: %s", err.Error())
		errM := mOpt.Update(ms)
		if errM != nil {
			s.tLogger.Error("update ms %s err %+v, after list subcriptions error", ms.TopicUrn, errM)
			return nil, errors.NewErrorRespWithMessage(errors.DbError, err.Error())
		}
		s.tLogger.Info("show topic urn %s without subscription", ms.TopicUrn)
		// 这种情况下如果添加订阅，会失败
		subNil := &smnModel.ListSubscriptionsByTopicResponse{
			Subscriptions: &[]smnModel.ListSubscriptionsItem{},
		}
		return s.BuildMessageResp(ms, subNil), nil
	}
	s.tLogger.Info("success show topic %s", ms.TopicUrn)
	return s.BuildMessageResp(ms, subs), nil
}

func (s *EventService) UpdateTopic(req *model.UpdateTopicRequest) *errors.ErrorResp {
	s.tLogger.Info("receive update topic request: project id %s", s.projectId)
	mOpt := db.GetMessageTableOpt()
	// 查询project上是否已存在主题
	ms, err := mOpt.Show(s.projectId)
	if err != nil {
		s.tLogger.Error("show topic from db err %+v, project id %s", err, s.projectId)
		return errors.NewErrorRespWithMessage(errors.DbError, err.Error())
	}
	ms.NoticeTraceStatus = strings.Join(req.NoticeTraceStatus, ",")
	err = mOpt.Update(ms)
	if err != nil {
		s.tLogger.Error("update topic error: %+v", err)
		return errors.NewErrorRespWithMessage(errors.DbError, err.Error())
	}
	return nil
}

// AddSubscription: 添加订阅
func (s *EventService) AddSubscription(topicUrn string, req *model.AddSubscriptionRequest) *errors.ErrorResp {
	s.tLogger.Info("receive add subscription request topic urn %s", topicUrn)
	mOpt := db.GetMessageTableOpt()
	ms, err := mOpt.Show(s.projectId)
	if err != nil {
		s.tLogger.Error("show topic from db err %+v, project id %s", err, s.projectId)
		return errors.NewErrorRespWithMessage(errors.DbError, err.Error())
	}
	if ms.TopicUrn != topicUrn {
		s.tLogger.Error("topic urn %s is not record topic urn %s in db", topicUrn, ms.TopicUrn)
		return errors.NewErrorResp(errors.TopicNotFound)
	}
	if err := s.CheckSubscriptionsCount(topicUrn); err != nil {
		s.tLogger.Error("check subscription topic err %s", err.Error())
		return errors.NewErrorRespWithMessage(errors.EventSmnError,
			fmt.Sprintf("check subscription count err: %s", err.Error()))
	}
	_, err = s.smnClient.AddSubscription(topicUrn, req)
	if err != nil {
		s.tLogger.Error("add subscription err: %+v", err)
		return errors.NewErrorRespWithMessage(errors.EventSmnError, fmt.Sprintf("add subscription err:%+v", err))
	}
	s.tLogger.Info("success add subscription %s topic urn %s", req.Endpoint, topicUrn)
	return nil
}

// CheckSubscriptionsCount: 检查主题的订阅者数量
func (s *EventService) CheckSubscriptionsCount(topicUrn string) error {
	limit := common.GetLimit(s.Ctx, s.tLogger)
	offset := common.GetOffset(s.Ctx, s.tLogger)
	subcriptionsRsp, err := s.smnClient.ListSubscriptionsByTopic(topicUrn, int32(limit), int32(offset))
	if err != nil {
		return err
	}
	if *subcriptionsRsp.SubscriptionCount >= DefaultMaxSubscriptionsCount {
		return fmt.Errorf("topic %s subscriptions count has been max %d, now %d",
			topicUrn, DefaultMaxSubscriptionsCount, *subcriptionsRsp.SubscriptionCount)
	}
	return nil
}

// DeleteSubscription: 取消订阅
func (s *EventService) CancelSubscription(subcriptionUrn string) *errors.ErrorResp {
	s.tLogger.Info("receive cancel subscription request,  subscrption urn %s", subcriptionUrn)
	_, err := s.smnClient.CancelSubscription(subcriptionUrn)
	if err != nil {
		s.tLogger.Error("cancel subscription err: %+v", err)
		return errors.NewErrorRespWithMessage(errors.EventSmnError, "cancel subscription err")
	}
	s.tLogger.Info("success to cancel subscription request, subscrption urn %s", subcriptionUrn)
	return nil
}

// PublishMessage: 发布消息
func (s *EventService) PublishMessage(subject string, event *db.Event) {
	s.tLogger.Info("[publish message] receive publish subject %s for event %s", subject, event.Id)
	mOpt := db.GetMessageTableOpt()
	// 查询project上是否已存在主题，存在则报错不能重复创建
	ms, err := mOpt.Show(s.projectId)
	if err != nil {
		s.tLogger.Error("[publish message] failed to publish event %s because show topic err %+v", event.Id, err)
		s.UpdateNoticeState(event, fmt.Sprintf("query topic err: %s", err.Error()))
		return
	}
	resp, err := s.smnClient.PublishMessage(ms.TopicUrn, subject, fmt.Sprintf("【SCASE】Event %s alert: %s", event.Id, event.SmnMessage))
	if err != nil {
		s.tLogger.Error("[publish message] failed to publish %+v for event %s", err, event.Id)
		s.UpdateNoticeState(event, fmt.Sprintf("publish message err: %s", err.Error()))
		return
	}
	s.UpdateNoticeState(event, fmt.Sprintf("success to publish message as %s", *resp.MessageId))
	s.tLogger.Info("[publish message] success to publish message to smn service, message id %s", *resp.MessageId)
}

// UpdateNoticeState: 更新通知状态
func (s *EventService) UpdateNoticeState(event *db.Event, noticeState string) {
	event.NoticeState = noticeState
	eOpt := db.GetEventTableOpt()
	err := eOpt.Update(event)
	if err != nil {
		s.tLogger.Error("[publish message] failed to update event notice state %+v for event %s", err, event.Id)
	}
}

// ClearEventBeforeDays: 清理事件数据,避免数据累积
func ClearEventBeforeDays(dayBefore int, tLogger *logger.FMLogger) {
	cond := orm.NewCondition()
	startTime := time.Now().AddDate(0, 0, -dayBefore)
	cond = cond.And(fmt.Sprintf("%s__lte", FieldRecordTime), startTime)
	eventOpt := db.GetEventTableOpt()
	events, err := eventOpt.List(cond, common.DefaultOffset, common.DefaultLimit)
	if err != nil {
		tLogger.Error("[clear event] list events from db err: %+v", err)
		return
	}

	tLogger.Info("[clear event] query %d expressed event data", len(*events))
	wg := &sync.WaitGroup{}
	wg.Add(len(*events))
	for idx, event := range *events {
		if idx%EventClearCountToSleep == 0 {
			time.Sleep(EventClearCountToSleepSeconds * time.Second)
		}
		go func(id string, wg *sync.WaitGroup) {
			defer wg.Done()
			err = eventOpt.Delete(id)
			if err != nil {
				tLogger.Error("[clear event] delete event %s from db error: %+v", id, err)
				return
			}
			tLogger.Info("[clear event] delete event %s success", id)
		}(event.Id, wg)
	}
	wg.Wait()
}
